#ifndef __QUATERNION_H__
#define __QUATERNION_H__

#pragma once

#include "Matrix3x3.h"

namespace Nezha
{

	class NEZHA_ENGINE_ENTRY Quaternion
	{
	public:
		Quaternion() {}
		Quaternion(Real fX, Real fY ,Real fZ, Real fW)
			:x(fX), y(fY), z(fZ), w(fW)
		{
		}

		Quaternion(const Vector3 &Axis, Real fAngle)
		{
			fromAngleAxis(fAngle, Axis);
		}

		Quaternion(const Vector3& r, const Vector3& u, const Vector3& d)
		{
			fromAxes(r, u, d);
		}

		Quaternion operator+ (const Quaternion &quat) const
		{
			return Quaternion(x+quat.x, y+quat.y,
				z+quat.z, w+quat.w);
		}

		Quaternion operator- (const Quaternion &quat) const
		{
			return Quaternion(x-quat.x, y-quat.y,
				z-quat.z, w-quat.w);
		}

		Quaternion operator- () const
		{
			return Quaternion(-x, -y, -z, -w);
		}

		Quaternion operator* (const Quaternion &quat) const;
		Quaternion operator* (Real c) const
		{
			return Quaternion(x*c, y*c, z*c, w*c);
		}

		Vector3 operator* (const Vector3& v) const;

		Quaternion& operator= (const Quaternion &q)
		{
			x = q.x;
			y = q.y;
			z = q.z;
			w = q.w;
			return *this;
		}

		bool operator== (const Quaternion &quat) const
		{
			return x == quat.x && y == quat.y && z == quat.z && w == quat.w;
		}

		bool operator!= (const Quaternion &quat) const
		{
			return x != quat.x || y != quat.y || z != quat.z || w != quat.w;
		}

		friend Quaternion operator* (Real c, const Quaternion &quat);

		Real sqrLength() const
		{
			return x*x + y*y + z*z + w*w;
		}

		Real length() const
		{
			return Sqrt(x*x + y*y + z*z + w*w);
		}

		void normalize();

		void fromAxes(const Vector3& r, const Vector3& u, const Vector3& d);
		void fromAngleAxis(Real fAngle, const Vector3 &Axis);
		void toAngleAxis(Real &fAngle, Vector3 &Axis) const;
		void toAxes(Vector3& r, Vector3& u, Vector3& d) const;

		void fromAnglesAxisX(Real fAngle);
		void fromAnglesAxisY(Real fAngle);
		void fromAnglesAxisZ(Real fAngle);

		void fromRotation(const Matrix3x3 &mat);
		void toRotation(Matrix3x3 &mat) const;

		void getRight(Vector3& r) const;
		void getUp(Vector3& u) const;
		void getDirection(Vector3& d) const;

		Quaternion inverse() const;

		static Real dot(const Quaternion &p, const Quaternion &q);
		static Quaternion unitInverse(const Quaternion &q);
		static Quaternion exp(const Quaternion &q);
		static Quaternion log(const Quaternion &q);
		static Quaternion slerp(Real t, const Quaternion &p, const Quaternion &q, bool shortestPath = false);
		static Quaternion squad(Real t, const Quaternion& q0, const Quaternion& a0,
			const Quaternion& a1, const Quaternion& q1);

		static Quaternion makeRotate(const Vector3& vFrom, const Vector3& vTo, const Vector3& fallbackAxis = Vector3::ZERO);

		static const Quaternion IDENTITY;

		Real w, x, y, z;
	};

	inline Quaternion operator* (Real c, const Quaternion &quat)
	{
		return Quaternion(c*quat.x, c*quat.y, c*quat.z, c*quat.w);
	}

}//end namespace Nezha

#endif //end __QUATERNION_H__